'------------------------------------------------------------------------------
' conspawn.inc - library for PowerBasic.
' Allows to launch, attach, get info from, and close a console exe
' In your callback:
'       - at the very beginning, call CON_CALLBACK
'       - spawn a console exe via CON_LAUNCH CB.HNDL, "myprog.exe", visible, tempo
'         (visible = 0|1 ; tempo is the frequence in ms to
'          check on the exe and call the CON_HANDLER() Sub)
'       - implement the following Sub:
DECLARE SUB CON_HANDLER(BYVAL ConContent AS STRING)
'         (ConContent is the content of the spawned console)
'------------------------------------------------------------------------------

#INCLUDE ONCE "WinCon.inc"

'------------------------------------------------------------------------------
GLOBAL g_Con_Prog   AS STRING
GLOBAL g_Con_Viso   AS LONG
GLOBAL g_Con_Freq   AS LONG
GLOBAL g_Con_Status AS LONG
'------------------------------------------------------------------------------
FUNCTION CON_LAUNCH(BYVAL hDlg AS DWORD, _
                    BYVAL prog AS STRING, _
                    BYVAL viso AS LONG, _
                    BYVAL freq AS LONG) AS LONG
    g_Con_Prog   = prog
    g_Con_Viso   = viso
    g_Con_Freq   = freq
    g_Con_Status = %STILL_ACTIVE

    DIALOG POST hDlg, %WM_APP, 0, 0
'    WHILE g_Con_Status = %STILL_ACTIVE
'        DIALOG DOEVENTS
'        SLEEP 0
'    WEND

    FUNCTION = g_Con_Status
END FUNCTION
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
MACRO CON_CALLBACK
    STATIC s_hCon, s_pid AS DWORD
    STATIC s_tmr, s_visu AS LONG
    LOCAL buf AS ASCIIZ * 512
    LOCAL file AS STRING
    LOCAL hInfo AS DWORD
    LOCAL conStatus AS LONG

    SELECT CASE AS LONG CB.MSG

        CASE %WM_APP
            ' Check that the exe we need to spawn actually exists
            conStatus = INSTR(LCASE$(g_Con_Prog), ".exe")
            IF conStatus <> 0 THEN
                buf = LEFT$(g_Con_Prog, conStatus + LEN(".exe")-1)
                buf = TRIM$(buf, $DQ)
                file = DIR$(buf) : DIR$ CLOSE
            END IF
            IF file = "" THEN
                g_Con_Status = 1
                CON_HANDLER "Fatal error: "+buf+" not found in " + EXE.PATH$
                SLEEP 1000
                CON_HANDLER "<end>"
                EXIT FUNCTION
            END IF
            ' Spawn exe via Shell
            s_pid = SHELL (g_Con_Prog, g_Con_Viso)
            SLEEP 1000
            ' Attach the corresponding console and get its handle
            AttachConsole(s_pid)
            s_hCon = GetStdHandle(%STD_OUTPUT_HANDLE)
            ' Start the timer to read console content at given frequence
            s_tmr = SetTimer(CB.HNDL, %WM_USER + 42, g_Con_Freq, BYVAL 0)
            DIALOG POST CB.HNDL, %WM_TIMER, %WM_USER + 42, 0

        CASE %WM_APP + 1
            IF s_pid = 0 THEN EXIT FUNCTION
            ' Attach the corresponding console and get its dialog handle
            AttachConsole(s_pid)
            hInfo = GetConsoleWindow()
            IF hInfo = 0 THEN EXIT FUNCTION
            s_visu = 1 - s_visu
            ShowWindow hInfo, IIF(ISTRUE s_visu, %SW_SHOW, %SW_HIDE)

        CASE %WM_DESTROY
            ' If parent dialog (this program) is killed then kill spawned process as well
            IF ISTRUE s_tmr THEN KillTimer CB.HNDL, s_tmr
            IF ISTRUE s_hCon THEN
                FreeConsole ()
                s_hCon = OpenProcess(%PROCESS_TERMINATE, %FALSE, s_pid)
                TerminateProcess(s_hCon, 0)
                CloseHandle(s_hCon)
            END IF
            ExitProcess(0)

        CASE %WM_TIMER
            IF CB.WPARAM = %WM_USER + 42 THEN
                ' Check status of spawn process
                hInfo = OpenProcess(%PROCESS_QUERY_INFORMATION, %FALSE, s_pid)
                GetExitCodeProcess(hInfo, conStatus)
                CloseHandle(hInfo)
                ' Spawn is finished: end timer + call CON_HANDLER one last time with empty buffer
                IF conStatus <> %STILL_ACTIVE THEN
                    KillTimer CB.HNDL, s_tmr : s_tmr = 0
                    FreeConsole () : s_hCon = 0
                    g_Con_Status = conStatus
                    CON_HANDLER "<end>"
                    EXIT FUNCTION
                END IF

                ' Else spawn is active: call CON_HANDLER with console content
                readConsoleOutputCharacter (s_hCon, BYVAL VARPTR (buf), 512, "", hInfo)
                CON_HANDLER buf

            END IF

    END SELECT
END MACRO
'------------------------------------------------------------------------------
